package top.zhaojx.rpc.transport.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import top.zhaojx.rpc.enumration.SerializerCode;
import top.zhaojx.rpc.transport.constants.RpcConstants;
import top.zhaojx.rpc.transport.dto.RpcMessage;
import top.zhaojx.rpc.transport.codec.serializer.CommonSerializer;

/**
 * 通用的编码拦截器
 * +---------------+---------------+-----------------+-------------+-------------+
 * |  Magic Number |  Full Length  |  Message Type   | Codec Type  | Request Id  |
 * |    4 bytes    |    4 bytes    |     1 bytes     |   1 bytes   |  4 bytes    |
 * +---------------+---------------+-----------------+-------------+-------------+
 * |                          Data Bytes                                         |
 * |                   Length: ${Data Length}                                    |
 * +---------------------------------------------------------------+-------------+
 * 首先是4字节魔数，标识一个协议包。
 * Full Length就是数据整体的长度，设置这个字段主要防止粘包
 * 接着是 Message Type标明这是一个调用请求还是调用响应，或是心跳包
 * Codec Type标明了实际数据使用的序列化器，这个服务端和客户端应当使用统一标准
 * Request Id唯一标识请求
 * 最后就是经过序列化后的实际数据，可能是 RpcRequest也可能是 RpcResponse经过序列化后的字节，取决于Message Type
 */
@Slf4j
public class CommonEncoder extends MessageToByteEncoder<RpcMessage>{

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, RpcMessage msg, ByteBuf byteBuf) throws Exception {
        byteBuf.writeBytes(RpcConstants.MAGIC_NUMBER);
        // leave a place to write the value of full length
        byteBuf.writerIndex(byteBuf.writerIndex() + 4);

        byte messageType = msg.getMessageType();
        byteBuf.writeByte(messageType);
        // serialize the object
        String codecName = SerializerCode.getName(msg.getCodec());
        log.info("codec name: [{}] ", codecName);
        CommonSerializer serializer = CommonSerializer.getByCode(msg.getCodec());
        byteBuf.writeByte(msg.getCodec());
        byteBuf.writeInt(msg.getRequestId());

        byte[] bodyBytes = serializer.serialize(msg.getData());
        if (bodyBytes != null) {
            byteBuf.writeBytes(bodyBytes);
        }
        int fullLength = RpcConstants.HEAD_LENGTH;
        fullLength += bodyBytes.length;

        int writeIndex = byteBuf.writerIndex();
        byteBuf.writerIndex(writeIndex - fullLength + RpcConstants.MAGIC_NUMBER.length);
        byteBuf.writeInt(fullLength);
        byteBuf.writerIndex(writeIndex);
    }
}
